package com.fable.common.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ZipUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.activation.MimetypesFileTypeMap;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 将文件中所有文件及文件夹压缩
 * source:  文件源的路径
 * dest:    压缩文件存放路径
 */
@Component
@Slf4j
public class ZipFileUtil {


    /**
     * 下载ZIP压缩包(会对下载后的压缩包进行删除)
     *
     * @param sourceFileName 文件夹目录
     * @param response       响应
     */
    public static void downloadZip(String zipName,String sourceFileName, HttpServletResponse response) {

        ZipOutputStream out = null;
        BufferedOutputStream bos = null;
        try {
            //将zip以流的形式输出到前台
            response.setHeader("content-type", MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setCharacterEncoding("utf-8");
            // 设置浏览器响应头对应的Content-disposition
//            response.setHeader("Content-disposition", "attachment;filename=" + new String(zipName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + ".zip");
            //防止文件名中文乱码
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipName, "UTF-8"));
            //创建zip输出流
            out = new ZipOutputStream(response.getOutputStream());
            //创建缓冲输出流
            bos = new BufferedOutputStream(out);
            File sourceFile = new File(sourceFileName);
            //调用压缩函数
            compress(out, sourceFile, sourceFile.getName());
            out.flush();
            //删除改临时zip包
            sourceFile.delete();
        } catch (Exception e) {
            log.error("下载zip压缩包过程发生异常:", e);
        } finally {
            close(bos, out);
        }

    }

    /**
     * 文件压缩
     *
     * @param out
     * @param sourceFile
     * @param base
     */
    public static void compress(ZipOutputStream out, File sourceFile, String base) {
        FileInputStream fos = null;
        BufferedInputStream bis = null;
        try {
            //如果路径为目录（文件夹）
            if (sourceFile.isDirectory()) {
                //取出文件夹中的文件（或子文件夹）
                File[] flist = sourceFile.listFiles();
                if (flist.length == 0) {//如果文件夹为空，则只需在目的地zip文件中写入一个目录进入点
                    out.putNextEntry(new ZipEntry(base + File.separator));
                } else {//如果文件夹不为空，则递归调用compress，文件夹中的每一个文件（或文件夹）进行压缩
                    for (File file : flist) {
                        compress(out, file, base + File.separator + file.getName());
                        file.delete();//删除子文件
                    }
                }
            } else {//如果不是目录（文件夹），即为文件，则先写入目录进入点，之后将文件写入zip文件中
                out.putNextEntry(new ZipEntry(base));
                fos = new FileInputStream(sourceFile);
                bis = new BufferedInputStream(fos);

                int tag;
                //将源文件写入到zip文件中
                while ((tag = bis.read()) != -1) {
                    out.write(tag);
                }
                bis.close();
                fos.close();
            }
        } catch (Exception e) {
            log.error("下载写入zip文件中过程发生异常:", e);
        } finally {
            close(bis, fos);
        }
    }

    /**
     * IO流关闭
     */
    public static void close(Closeable... io) {
        for (Closeable temp : io) {
            try {
                if (null != temp)
                    temp.close();
            } catch (IOException e) {
                log.error("关闭流失败:", e);
            }
        }
    }

    /**
     * 生成Zip压缩包 (注意是对hutool的二次封装,所以必须要有hutool依赖)
     *
     * @param targetZipFile 要生成的目标zip压缩包
     * @param sourceDir   压缩目录中包含的文件集合
     * @param dirWithFlag   是否将文件目录一同打包进去 (true:压缩包中包含文件目录,false:压缩包中不包含目录)
     */
    public static void generateZip(File targetZipFile, List<File> sourceDir, boolean dirWithFlag,boolean isDelFile) {
        if (CollUtil.isNotEmpty(sourceDir)) {
            File[] fileArr = sourceDir.toArray(new File[]{});
            ZipUtil.zip(targetZipFile, dirWithFlag, fileArr);
            if(isDelFile){
                for (File file : sourceDir) {
                    file.delete();
                }
            }
        }
    }

    /**
     * 下载ZIP压缩包(会对下载后的压缩包进行删除)
     *
     * @param file     zip压缩包文件
     * @param response 响应
     */
    public static void downloadZip(File file, HttpServletResponse response) {
        OutputStream toClient = null;
        try {
            // 以流的形式下载文件。
            BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file.getPath()));
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();
            // 清空response
            response.reset();
            toClient = new BufferedOutputStream(response.getOutputStream());
            response.setCharacterEncoding("UTF-8");
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
            toClient.write(buffer);
            toClient.flush();
        } catch (Exception e) {
            log.error("下载zip压缩包过程发生异常:", e);
        } finally {
            close(toClient);
            //删除改临时zip包(此zip包任何时候都不需要保留,因为源文件随时可以再次进行压缩生成zip包)
            file.delete();
        }
    }

    /**
     * 任何单文件下载
     *
     * @param file     要下载的文件
     * @param response 响应
     */
    public static void downloadOneFile(File file, HttpServletResponse response) {
        FileInputStream fileInputStream = null;
        OutputStream outputStream = null;
        try {
            fileInputStream = new FileInputStream(file);
            // 清空response
            response.reset();
            //防止文件名中文乱码
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
            //根据文件动态setContentType
            response.setContentType(new MimetypesFileTypeMap().getContentType(file) + ";charset=UTF-8");
            outputStream = response.getOutputStream();
            byte[] bytes = new byte[2048];
            int len;
            while ((len = fileInputStream.read(bytes)) > 0) {
                outputStream.write(bytes, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            close(outputStream, fileInputStream);
        }
    }

}
